use actix_web::{web, HttpResponse, Result};
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};

use crate::{
    database::{Database, health_check},
    config::AppConfig,
};

#[derive(Serialize, Deserialize)]
pub struct HealthResponse {
    pub status: String,
    pub timestamp: DateTime<Utc>,
    pub version: String,
    pub database: DatabaseHealth,
    pub uptime: String,
}

#[derive(Serialize, Deserialize)]
pub struct DatabaseHealth {
    pub status: String,
    pub response_time_ms: Option<u64>,
}

static START_TIME: once_cell::sync::Lazy<DateTime<Utc>> = once_cell::sync::Lazy::new(|| Utc::now());

pub async fn health_check_handler(
    db: web::Data<Database>,
    _config: web::Data<AppConfig>,
) -> Result<HttpResponse> {
    let start = std::time::Instant::now();
    let db_healthy = health_check(&db).await;
    let db_response_time = start.elapsed().as_millis() as u64;
    
    let uptime = Utc::now().signed_duration_since(*START_TIME);
    let uptime_str = format!(
        "{}d {}h {}m {}s",
        uptime.num_days(),
        uptime.num_hours() % 24,
        uptime.num_minutes() % 60,
        uptime.num_seconds() % 60
    );

    let health = HealthResponse {
        status: if db_healthy { "healthy".to_string() } else { "unhealthy".to_string() },
        timestamp: Utc::now(),
        version: env!("CARGO_PKG_VERSION").to_string(),
        database: DatabaseHealth {
            status: if db_healthy { "connected".to_string() } else { "disconnected".to_string() },
            response_time_ms: if db_healthy { Some(db_response_time) } else { None },
        },
        uptime: uptime_str,
    };

    let status_code = if db_healthy { 200 } else { 503 };
    
    Ok(HttpResponse::build(actix_web::http::StatusCode::from_u16(status_code).unwrap())
        .json(health))
}

pub async fn readiness_check(
    db: web::Data<Database>,
) -> Result<HttpResponse> {
    let db_healthy = health_check(&db).await;
    
    if db_healthy {
        Ok(HttpResponse::Ok().json(serde_json::json!({
            "status": "ready",
            "timestamp": Utc::now()
        })))
    } else {
        Ok(HttpResponse::ServiceUnavailable().json(serde_json::json!({
            "status": "not ready",
            "timestamp": Utc::now(),
            "reason": "database not available"
        })))
    }
}

pub async fn liveness_check() -> Result<HttpResponse> {
    Ok(HttpResponse::Ok().json(serde_json::json!({
        "status": "alive",
        "timestamp": Utc::now()
    })))
}

pub fn configure(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::scope("/health")
            .route("", web::get().to(health_check_handler))
            .route("/ready", web::get().to(readiness_check))
            .route("/live", web::get().to(liveness_check))
    );
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::{test, App};
    use crate::config::AppConfig;

    #[actix_web::test]
    async fn test_liveness_check() {
        let app = test::init_service(
            App::new().configure(configure)
        ).await;

        let req = test::TestRequest::get()
            .uri("/health/live")
            .to_request();
            
        let resp = test::call_service(&app, req).await;
        assert!(resp.status().is_success());
    }
}